import os,re,sys
import logging

DIR_PATH = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
PYGMENTS_PATH = os.path.join(DIR_PATH,'pygments.zip')

sys.path.insert(0,PYGMENTS_PATH)
from pygments import highlight
from pygments.lexers import get_lexer_by_name,guess_lexer,get_all_lexers,find_lexer_class
from pygments.styles import get_all_styles
from pygments.formatter import Formatter

from waveapi.document import Range


class WaveFormatter(Formatter):
    def __init__(self,blip,range, **options):
        Formatter.__init__(self, **options)
        self.blip = blip
        self.cur_position = range.start

        self.styles = {}
        self._make_styles()

    def _make_styles(self):
        for ttype, ndef in self.style:
            wavestyles = {}
            if ndef['color']:
                wavestyles["style/color"] = "#"+ndef['color']
            if ndef['bold']:
                wavestyles["style/fontWeight"] = "bold"      
            if ndef['italic']:
                wavestyles["style/fontStyle"] = "italic"     
            if ndef['underline']:
                wavestyles["style/textDecoration"] = "underline" 
            if ndef['bgcolor']:
                wavestyles["style/backgroundColor"] = "#"+ndef['bgcolor']

            self.styles[ttype] = wavestyles  
 
    def setWaveAnnotation(self, range, wavestyles):
        for name in wavestyles:
            value = wavestyles[name]
            self.blip.SetAnnotation(range, name, value)
            #logging.info("setWaveAnnotation:%s %s %s "%(range,name,value)) 

    def format(self, tokensource, outfile):        
        for tokentype, tokenvalue in tokensource:
            while tokentype and (tokentype not in self.styles):
                tokentype = tokentype.parent
                
            #logging.info("cur_position:%d %s  %s "%(self.cur_position,tokentype,tokenvalue))
            
            if tokentype in self.styles:    
                start = self.cur_position
                end = start + len(tokenvalue)
                range = Range(start, end)
                self.setWaveAnnotation(range, self.styles[tokentype])
                
            self.cur_position += len(tokenvalue)
        return ""
    
def getSupportedLang():
    languages = [lexer[1][0] for lexer in get_all_lexers()]
    languages.sort()
    return languages

def getSupportedStyles():
    return list(get_all_styles())

def cleanCodeAnnotations(blip, code_range):
    blip.DeleteAnnotationsInRange(code_range, "style/color")
    blip.DeleteAnnotationsInRange(code_range, "style/fontWeight")
    blip.DeleteAnnotationsInRange(code_range, "style/fontStyle")
    blip.DeleteAnnotationsInRange(code_range, "style/textDecoration")
    blip.DeleteAnnotationsInRange(code_range, "style/backgroundColor")
    blip.DeleteAnnotationsInRange(code_range, "style/fontFamily")
    
def initCodeAnnotations(blip, code_range, lang = "text"):
    blip.SetAnnotation(code_range, "codelang", lang)
    blip.SetAnnotation(code_range, "wavehighlight", "True")
    blip.SetAnnotation(code_range, "style/fontFamily", "Courier New,monospace")
      
def getAnnotationsByName(blip, name):
    annotations = []
    for annotation in blip._blip.annotations:
        if annotation.name == name:
            annotations.append(annotation)
    return annotations

def setAnnotations(blip, annotations):
    for annotation in annotations:
        blip.SetAnnotation(annotation.range, annotation.name , annotation.value)
      
re_source = re.compile(r"\[code\s*(lang='(?P<lang>\S+)'){0,1}\s*(style='(?P<style>\S+)'){0,1}\](\s*)(?P<source>.+?)\[/code\]",re.DOTALL)
  
def highlightBlip(blip):
    matches = list(re_source.finditer(blip.GetText()))
    for match in matches:
        code_range = match.span()
        code_range = Range(code_range[0], code_range[1])
        
        lang = match.group('lang') or None
        style = match.group('style') or 'default'
        source = match.group('source')
        
        source_range = match.span('source')
        source_range = Range(source_range[0], source_range[1])
        
        try:
            lexer = get_lexer_by_name(lang)
        except:
            try:
                lexer = guess_lexer(source)
                lang = lexer.aliases[0]
            except:
                lexer = get_lexer_by_name("text")
                lang = "text"
                
        logging.info("HL: %s %s %s \n%s"%(code_range,lang,style,source))
        
        fontSize = getAnnotationsByName(blip, "style/fontSize")
        cleanCodeAnnotations(blip, code_range)
        initCodeAnnotations(blip, code_range, lang)
        setAnnotations(blip, fontSize)
           
        try:        
            formatter = WaveFormatter(blip=blip,
                                       range=source_range,         
                                       style=style)
            
            highlight(source, lexer, formatter)
        except:
            logging.exception('formatter exception')
            
        

